/**
 * Procesadores de Lenguaje, curso 2009-2010
 * @author grupo 02
 *
 */

package traductor;

import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;

public class AnalizadorLexico {
    //Categorias lexicas asociadas a cada token
    public static enum CatLexica {IDENT, SIMBOLO, CERO, NUM_NAT, NUM_FLOAT, CTE_CHAR, MAS, 
                                  MENOS, FLECHA, MULT, DIV, MOD, BARRA, MENOR, MAYOR, MENOR_IGUAL, MAYOR_IGUAL,
                                  IGUAL, DISTINTO, DESP_DER, DESP_IZQ, DOS_PUNTOS, ASIG, EOF, PUNTO,
                                  COMA, PYCOMA, SEP, PAP, PCIE, INTEGER, NATURAL, FLOAT, BOOLEAN, CHAR, TRUE,
                                  FALSE, AND, OR, NOT, IN, OUT, CONV_NAT, CONV_INT, CONV_CHAR, CONV_FLOAT, LL_AP,
                                  LL_CIE, COR_AP, COR_CIE, IF, THEN, ELSE, WHILE, DO, FOR, TO, TIPO, ARRAY, OF,
                                  RECORD, POINTER, NULL, NEW, DISPOSE, PROC, VAR, FORWARD}

    //Estados en los cuales se analizan los caracteres
    private static enum Estado {INICIO, LEYENDO_IDENT, LEYENDO_SIMBOLO, LEYENDO_PUNTO, LEYENDO_COMA, 
                                LEYENDO_PYCOMA, LEYENDO_PAP, LEYENDO_PCIE, LEYENDO_SEP, LEYENDO_CERO,
                                LEYENDO_INT, LEYENDO_NAT, LEYENDO_FLOAT, LEYENDO_COMILLA, LEYENDO_CHAR,
                                LEYENDO_MAS, LEYENDO_MENOS, LEYENDO_FLECHA, LEYENDO_MULT, LEYENDO_DIV, LEYENDO_MOD,
                                LEYENDO_BARRA, LEYENDO_MENOR, LEYENDO_MAYOR, LEYENDO_IGUAL, LEYENDO_DISTINTO,
                                LEYENDO_CASTING, LEYENDO_DI, LEYENDO_DD, LEYENDO_2P, LEYENDO_ASIG,
                                LEYENDO_FIN_LINEA, LEYENDO_EXPONENTE, LEYENDO_LLAP, LEYENDO_LLCIE, LEYENDO_CORAP,
                                LEYENDO_CORCIE, FIN}

    private BufferedReader fichero; //Fichero fuente a analizar
    private StringBuffer lex; //lexema de cada token creado
    private int sigCar; //Siguiente caracter leido del fichero
    private Estado estado; //Estado en el que nos encontramos al leer el fichero
    private static int num_linea; //Linea actual del archivo sobre la que estamos leyendo
    private static int num_col; //Columna actual del archivo sobre la que estamos leyendo
    private boolean parte_dec; //Nos indica si hay decimales en el token en analisis. Utilizado para eliminar ceros a la derecha

    public AnalizadorLexico(FileReader file) {
        fichero = new BufferedReader(file);
        lex = new StringBuffer();
        num_linea = 1;  
        num_col = 1;   
        parte_dec = false;
    }

    public static int getNumLineaActual() {
        return num_linea;
    }
    public static int getNumColActual() {
        return num_col;
    }

    public void inicializa() throws IOException {
        sigCar = fichero.read();
    }

    //Se analizan los caracteres leidos y segun sea se pasa al estado que corresponde
    public Token sigToken() throws IOException {
        estado = Estado.INICIO;
        lex.delete(0, lex.length());
        while (true) {
            switch (estado) {
                case INICIO:
                    if (hayLetra()) transita(Estado.LEYENDO_IDENT);
                    else if (haySimbolo()) transita(Estado.LEYENDO_SIMBOLO);
                    else if (hayPunto()) transita(Estado.LEYENDO_PUNTO);
                    else if (hayComa()) transita(Estado.LEYENDO_COMA);
                    else if (hayPuntoYComa()) transita(Estado.LEYENDO_PYCOMA);
                    else if (hayPAp()) transitaMarcando(Estado.LEYENDO_PAP);
                    else if (hayPCie()) transita(Estado.LEYENDO_PCIE);
                    else if (haySep()) transita(Estado.LEYENDO_SEP);
                    else if (hayCero()) transita(Estado.LEYENDO_CERO);
                    else if (hayNum()) transitaMarcando(Estado.LEYENDO_NAT);
                    else if (haySuma()) transita(Estado.LEYENDO_MAS);
                    else if (hayMenos()) transita(Estado.LEYENDO_MENOS);
                    else if (hayMult()) transita(Estado.LEYENDO_MULT);
                    else if (hayDiv()) transita(Estado.LEYENDO_DIV);
                    else if (hayMod()) transita(Estado.LEYENDO_MOD);
                    else if (hayBarra()) transita(Estado.LEYENDO_BARRA);
                    else if (hayComilla()) transitaMarcando(Estado.LEYENDO_COMILLA);
                    else if (hayMenor()) transita(Estado.LEYENDO_MENOR);
                    else if (hayMayor()) transita(Estado.LEYENDO_MAYOR);
                    else if (hayIgual()) transitaMarcando(Estado.LEYENDO_IGUAL);
                    else if (hay2Puntos()) transita(Estado.LEYENDO_2P);
                    else if (hayLlAp()) transita(Estado.LEYENDO_LLAP);
                    else if (hayLlCie()) transita(Estado.LEYENDO_LLCIE);
                    else if (hayCorAp()) transita(Estado.LEYENDO_CORAP);
                    else if (hayCorCie()) transita(Estado.LEYENDO_CORCIE);
                    else if (hayIgnorable()) transitaIgnorando(Estado.INICIO);
                    else if (hayComentario()) transitaIgnorandoLinea(Estado.INICIO);
                    else if (hayFinDeLinea()) {
                        transitaIgnorando(Estado.INICIO);
                        num_linea++;
                        num_col = 1;
                    }
                    else if (hayEof()) terminar();
                    else error();
                    break;
                case LEYENDO_IDENT:
                    if (hayAlfanumerico()) transita(Estado.LEYENDO_IDENT);
                    else return actualizaYDevuelve(analizaPalabra(lex.toString()), lex.length());
                    break;
                case LEYENDO_SIMBOLO:
                    return actualizaYDevuelve(new Token(CatLexica.SIMBOLO, lex.toString()), lex.length());
                case LEYENDO_PUNTO:
                    return actualizaYDevuelve(new Token(CatLexica.PUNTO, lex.toString()), lex.length());
                case LEYENDO_COMA:
                    return actualizaYDevuelve(new Token(CatLexica.COMA, lex.toString()), lex.length());
                case LEYENDO_PYCOMA:
                    return actualizaYDevuelve(new Token(CatLexica.PYCOMA, lex.toString()), lex.length());
                case LEYENDO_PAP:
                    if (hayLetra()) transita(Estado.LEYENDO_CASTING);
                    else return actualizaYDevuelve(new Token(CatLexica.PAP, lex.toString()), lex.length());
                    break;
                case LEYENDO_CASTING:
                    //Comprueba si hay parentesis de apertura, una secuencia de letras y parentesis de cierre.
                    //Si todo ello forma un casting, crea el token. Si no, devuelve el parentesis de apertura
                    //como token independiente y pone el puntero de lectura en la primera letra.
                    if (hayLetra()) transita(Estado.LEYENDO_CASTING);
                    else if (hayPCie()) {
                        Token s = analizaCasting(lex.toString());
                        if (s != null) {
                            sigCar = fichero.read();
                            return actualizaYDevuelve(s, s.getLex().length());
                        }
                        fichero.reset();
                        sigCar = fichero.read();
                        return actualizaYDevuelve(new Token(CatLexica.PAP, "("), 1);
                    }
                    else {
                        fichero.reset();
                        sigCar = fichero.read();
                        return actualizaYDevuelve(new Token(CatLexica.PAP, "("), 1);
                    }
                    break;
                case LEYENDO_PCIE:
                    return actualizaYDevuelve(new Token(CatLexica.PCIE, lex.toString()), lex.length());
                case LEYENDO_SEP:
                    return actualizaYDevuelve(new Token(CatLexica.SEP, lex.toString()), lex.length());
                case LEYENDO_CERO:
                    //Solamente podran ser los numeros de tipo float los que comiencen por cero
                    if (hayPunto()) transita(Estado.LEYENDO_FLOAT);
                    else return actualizaYDevuelve(new Token(CatLexica.CERO, lex.toString()), lex.length());
                    break;
                case LEYENDO_NAT:
                    //En cuanto se encuentra un punto, el numero pasa a ser de tipo float.
                    if (hayNum()) transitaMarcando(Estado.LEYENDO_NAT);
                    else if (hayPunto() && hayNumDespues()) transitaMarcando(Estado.LEYENDO_FLOAT);
                    else if (hayExponente() && (haySignoDespues() || hayNumDespuesDistintoDeCero())) transita(Estado.LEYENDO_EXPONENTE);
                    else return actualizaYDevuelve(new Token(CatLexica.NUM_NAT, lex.toString()), lex.length());
                    break;
                case LEYENDO_FLOAT:
                    //No puede haber ceros a la derecha en un numero de tipo float.
                    if (hayCero()) {
                        if (hayIgnorableDespues() || hayFinDeLineaDespues() || hayExponenteDespues()) {
                            //Si el cero a la derecha es el primer decimal, hay que eliminar el punto del lexema y volver a leerlo
                            if (parte_dec == false) {
                                fichero.reset();
                                sigCar = lex.charAt(lex.length() - 1);
                                lex.deleteCharAt(lex.length() - 1);
                            }
                            return actualizaYDevuelve(new Token(CatLexica.NUM_FLOAT, lex.toString()), lex.length());
                        }
                        else transitaMarcando(Estado.LEYENDO_FLOAT);
                    }
                    else if (hayNum()) {
                        parte_dec = true;
                        transitaMarcando(Estado.LEYENDO_FLOAT);
                    }
                    else if (hayExponente() && (haySignoDespues() || hayNumDespuesDistintoDeCero())) transita(Estado.LEYENDO_EXPONENTE);
                    else return actualizaYDevuelve(new Token(CatLexica.NUM_FLOAT, lex.toString()), lex.length());
                    break;
                case LEYENDO_EXPONENTE:
                    if (hayNum()) transitaMarcando(Estado.LEYENDO_FLOAT);
                    else if (haySigno() && hayNumDespuesDistintoDeCero()) {
                        sigCar = fichero.read();
                        transita(Estado.LEYENDO_FLOAT);                        
                    }
                    else {
                        fichero.reset();
                        sigCar = fichero.read();
                        lex.deleteCharAt(lex.length() - 1);
                        return actualizaYDevuelve(new Token(CatLexica.NUM_FLOAT, lex.toString()), lex.length());
                    }
                    break;
                case LEYENDO_MAS:
                    return actualizaYDevuelve(new Token(CatLexica.MAS, lex.toString()), lex.length());
                case LEYENDO_MENOS:
                    if (hayMayor()) transita(Estado.LEYENDO_FLECHA);
                    else return actualizaYDevuelve(new Token(CatLexica.MENOS, lex.toString()), lex.length());
                case LEYENDO_FLECHA:
                    return actualizaYDevuelve(new Token(CatLexica.FLECHA, lex.toString()), lex.length());
                case LEYENDO_MULT:
                    return actualizaYDevuelve(new Token(CatLexica.MULT, lex.toString()), lex.length());
                case LEYENDO_DIV:
                    return actualizaYDevuelve(new Token(CatLexica.DIV, lex.toString()), lex.length());
                case LEYENDO_MOD:
                    return actualizaYDevuelve(new Token(CatLexica.MOD, lex.toString()), lex.length());
                case LEYENDO_BARRA:
                    return actualizaYDevuelve(new Token(CatLexica.BARRA, lex.toString()), lex.length());
                case LEYENDO_COMILLA:
                    if ((haySimbolo() || hayAlfanumerico() || hayPunto() || hayComa() || hayPuntoYComa() || hay2Puntos())
                        && hayComillaFinal()) transita(Estado.LEYENDO_CHAR);
                    else return actualizaYDevuelve(new Token(CatLexica.SIMBOLO, lex.toString()), lex.length());
                    break;
                case LEYENDO_CHAR:
                    //Devuelve una constante char si encuentra comilla simple, uno de los simbolos requeridos y otra comilla simple
                    if (hayComilla()) transita(Estado.LEYENDO_CHAR);
                    else return actualizaYDevuelve(new Token(CatLexica.CTE_CHAR, lex.toString()), lex.length());
                    break;
                case LEYENDO_MENOR:
                    if (hayMenor()) transita(Estado.LEYENDO_DI);
                    else return actualizaYDevuelve(new Token(CatLexica.MENOR, lex.toString()), lex.length());
                    break;
                case LEYENDO_MAYOR:
                    if (hayMayor()) transita(Estado.LEYENDO_DD);
                    else return actualizaYDevuelve(new Token(CatLexica.MAYOR, lex.toString()), lex.length());
                    break;
                case LEYENDO_DI:
                    return actualizaYDevuelve(new Token(CatLexica.DESP_IZQ, lex.toString()), lex.length());
                case LEYENDO_DD:
                    return actualizaYDevuelve(new Token(CatLexica.DESP_DER, lex.toString()), lex.length());
                case LEYENDO_IGUAL:
                    //Si hay una division y un igual despues de esta, se crea un token distinto. Si no, se devuelve un igual por separado
                    if (hayDiv() && hayIgualFinal()) transita(Estado.LEYENDO_DISTINTO);
                    else return actualizaYDevuelve(new Token(CatLexica.IGUAL, lex.toString()), lex.length());
                    break;
                case LEYENDO_DISTINTO:
                    if (hayIgual()) transita(Estado.LEYENDO_DISTINTO);
                    else return actualizaYDevuelve(new Token(CatLexica.DISTINTO, lex.toString()), lex.length());
                    break;
                case LEYENDO_2P:
                    if (hayIgual()) transita(Estado.LEYENDO_ASIG);
                    else return actualizaYDevuelve(new Token(CatLexica.DOS_PUNTOS, lex.toString()), lex.length());
                    break;
                //Llaves de apertura y cierre
                case LEYENDO_LLAP:
                    return actualizaYDevuelve(new Token(CatLexica.LL_AP, lex.toString()), lex.length());
                case LEYENDO_LLCIE:
                    return actualizaYDevuelve(new Token(CatLexica.LL_CIE, lex.toString()), lex.length());
                //Corchetes de apertura y cierre
                case LEYENDO_CORAP:
                    return actualizaYDevuelve(new Token(CatLexica.COR_AP, lex.toString()), lex.length());
                case LEYENDO_CORCIE:
                    return actualizaYDevuelve(new Token(CatLexica.COR_CIE, lex.toString()), lex.length());
                case LEYENDO_ASIG:
                    return actualizaYDevuelve(new Token(CatLexica.ASIG, lex.toString()), lex.length());
                case FIN: 
                    System.out.println("Analisis lexico finalizado correctamente\n");
                    return new Token(CatLexica.EOF, lex.toString());
            }
        }
    }
    //Fin de sigToken

    //Este metodo se encarga de devolver el token creado y de sumar su numero de caracteres al contador de columna
    private Token actualizaYDevuelve(Token t, int n) {
        num_col += n;
        parte_dec = false;
        return t;
    }

    public String leeLexema() {
        return lex.toString();
    }

    private boolean hayLetra() {
        return sigCar >= 'a' && sigCar <= 'z' ||
               sigCar >= 'A' && sigCar <= 'Z' ||
               sigCar == 'ñ' || sigCar == 'Ñ';
    }
    private boolean hayExponente() {
        return sigCar == 'e' || sigCar == 'E';
    }
    //Comprueba si hay exponente en el siguiente caracter a leer
    //y devuelve el puntero de lectura al caracter anteriormente marcado
    private boolean hayExponenteDespues() throws IOException {
        boolean hay = false;
        sigCar = fichero.read();
        if (hayExponente())
            hay = true;
        fichero.reset();
        sigCar = fichero.read();
        return hay;
    }
    private boolean haySimbolo() {
        return sigCar == '_' || sigCar == '^' ||
               sigCar == '$' || sigCar == '~' ||
               sigCar == '@' || sigCar == '€' || sigCar == '?';
    }
    private boolean hayPunto() {
        return sigCar == '.';
    }
    private boolean hayComa() {
        return sigCar == ',';
    }
    private boolean hayComilla() {
        return sigCar == 39;
    }
    //Comprueba si hay comilla simple en el siguiente caracter a leer
    //y devuelve el puntero de lectura al caracter anteriormente marcado
    private boolean hayComillaFinal() throws IOException {
        boolean hay = false;
        sigCar = fichero.read();
        if (hayComilla())
            hay = true;
        fichero.reset();
        sigCar = fichero.read();
        return hay;
    }
    private boolean hayPuntoYComa() {
        return sigCar == ';';
    }
    private boolean hayPAp() {
        return sigCar == '(';
    }
    private boolean hayPCie() {
        return sigCar == ')';
    }
    private boolean haySep() {
        return sigCar == '&';
    }
    private boolean hayCero() {
        return sigCar == '0';
    }
    private boolean hayNum() {
        return sigCar >= '0' && sigCar <= '9';
    }
    //Comprueba si hay numero en el siguiente caracter a leer
    //y devuelve el puntero de lectura al caracter anteriormente marcado
    private boolean hayNumDespues() throws IOException {
        boolean hay = false;
        sigCar = fichero.read();
        if (hayNum())
            hay = true;
        fichero.reset();
        sigCar = fichero.read();
        return hay;
    }
    //Comprueba si hay un numero distinto de cero en el siguiente caracter a leer
    //y devuelve el puntero de lectura al caracter anteriormente marcado
    private boolean hayNumDespuesDistintoDeCero() throws IOException {
        boolean hay = false;
        sigCar = fichero.read();
        if (hayNum() && !hayCero())
            hay = true;
        fichero.reset();
        sigCar = fichero.read();
        return hay;
    }
    private boolean haySigno() {
        return sigCar == '+' || sigCar == '-';
    }
    //Comprueba si hay signo en el siguiente caracter a leer
    //y devuelve el puntero de lectura al caracter anteriormente marcado
    private boolean haySignoDespues() throws IOException {
        boolean hay = false;
        sigCar = fichero.read();
        if (haySigno())
            hay = true;
        fichero.reset();
        sigCar = fichero.read();
        return hay;
    }
    private boolean haySuma() {
        return sigCar == '+';
    }
    private boolean hayMenos() {
        return sigCar == '-';
    }
    private boolean hayMult() {
        return sigCar == '*';
    }
    private boolean hayDiv() {
        return sigCar == '/';
    }
    private boolean hayMod() {
        return sigCar == '%';
    }
    private boolean hayBarra() {
        return sigCar == '|';
    }
    private boolean hayMenor() {
        return sigCar == '<';
    }
    private boolean hayMayor() {
        return sigCar == '>';
    }
    private boolean hayIgual() {
        return sigCar == '=';
    }
    //Comprueba si hay un igual en el siguiente caracter a leer
    //y devuelve el puntero de lectura al caracter anteriormente marcado
    private boolean hayIgualFinal() throws IOException {
        boolean hay = false;
        sigCar = fichero.read();
        if (hayIgual())
            hay = true;
        fichero.reset();
        sigCar = fichero.read();
        return hay;
    }
    private boolean hay2Puntos() {
        return sigCar == ':';
    }
    private boolean hayLlAp() {
        return sigCar == '{';
    }
    private boolean hayLlCie() {
        return sigCar == '}';
    }
    private boolean hayCorAp() {
        return sigCar == '[';
    }
    private boolean hayCorCie() {
        return sigCar == ']';
    }
    private boolean hayAlfanumerico() {
        return hayLetra() || hayNum();
    }
    private boolean hayEof() {
        return sigCar == -1;
    }
    private boolean hayIgnorable() {
        if (sigCar == '\t') { //El tabulado lo contamos como 4 columnas
            num_col+=4;
            return true;
        }
        else if (sigCar == ' ' || sigCar == '\r' || sigCar == '\b') {
            num_col++;
            return true;
        }
        return false;
    }
    //Comprueba si hay caracter ignorable en el siguiente caracter a leer
    //y devuelve el puntero de lectura al caracter anteriormente marcado
    private boolean hayIgnorableDespues() throws IOException {
        boolean hay = false;
        sigCar = fichero.read();
        if (hayIgnorable())
            hay = true;
        fichero.reset();
        sigCar = fichero.read();
        return hay;
    }
    private boolean hayComentario() {
        return sigCar == '#';
    }
    private boolean hayFinDeLinea() {
        return sigCar == '\n';
    }
    //Comprueba si hay fin de linea en el siguiente caracter a leer
    //y devuelve el puntero de lectura al caracter anteriormente marcado
    private boolean hayFinDeLineaDespues() throws IOException {
        boolean hay = false;
        sigCar = fichero.read();
        if (hayFinDeLinea())
            hay = true;
        fichero.reset();
        sigCar = fichero.read();
        return hay;
    }
    //Transita al siguiente estado añadiendo al lexema el caracter actual y despues lee el siguiente
    private void transita(Estado sig) throws IOException {
        lex.append((char)sigCar);
        sigCar = fichero.read();
        estado = sig;
    }
    //Transita al siguiente estado añadiendo al lexema el caracter actual, marcandolo antes de leer el siguiente
    private void transitaMarcando(Estado sig) throws IOException {
        lex.append((char)sigCar);
        fichero.mark(1); //Marcamos este caracter para poder volver a leerlo
        sigCar = fichero.read();
        estado = sig;
    }
    //Transita al siguiente estado sin añadir el caracter actual al lexema.
    private void transitaIgnorando(Estado sig) throws IOException {
        sigCar = fichero.read();
        estado = sig;
    }
    //Transita al siguiente estado ignorando todo el resto de la linea
    private void transitaIgnorandoLinea(Estado sig) throws IOException {
        while (sigCar != '\n' && sigCar != -1)
            sigCar = fichero.read();
        estado = sig;
    }
    private void terminar() {
        estado = Estado.FIN;
    }
    
    private void error() {
        System.err.println("ERROR EN ANALISIS LEXICO: Caracter desconocido: " + (char)sigCar + "\t(Linea " + num_linea + ", columna " + num_col + ")");
        System.exit(1);
    }

    //Comprueba si la palabra es reservada y devuelve el token que corresponda
    private Token analizaPalabra(String s) {
        if (s.equals("integer")) return new Token(CatLexica.INTEGER, s.toString());
        if (s.equals("natural")) return new Token(CatLexica.NATURAL, s.toString());
        if (s.equals("float")) return new Token(CatLexica.FLOAT, s.toString());
        if (s.equals("boolean")) return new Token(CatLexica.BOOLEAN, s.toString());
        if (s.equals("character")) return new Token(CatLexica.CHAR, s.toString());
        if (s.equals("true")) return new Token(CatLexica.TRUE, s.toString());
        if (s.equals("false")) return new Token(CatLexica.FALSE, s.toString());
        if (s.equals("and")) return new Token(CatLexica.AND, s.toString());
        if (s.equals("or")) return new Token(CatLexica.OR, s.toString());
        if (s.equals("not")) return new Token(CatLexica.NOT, s.toString());
        if (s.equals("in")) return new Token(CatLexica.IN, s.toString());
        if (s.equals("out")) return new Token(CatLexica.OUT, s.toString());
        if (s.equals("if")) return new Token(CatLexica.IF, s.toString());
        if (s.equals("then")) return new Token(CatLexica.THEN, s.toString());
        if (s.equals("else")) return new Token(CatLexica.ELSE, s.toString());
        if (s.equals("while")) return new Token(CatLexica.WHILE, s.toString());
        if (s.equals("do")) return new Token(CatLexica.DO, s.toString());
        if (s.equals("for")) return new Token(CatLexica.FOR, s.toString());
        if (s.equals("to")) return new Token(CatLexica.TO, s.toString());
        if (s.equals("tipo")) return new Token(CatLexica.TIPO, s.toString());
        if (s.equals("array")) return new Token(CatLexica.ARRAY, s.toString());
        if (s.equals("of")) return new Token(CatLexica.OF, s.toString());
        if (s.equals("record")) return new Token(CatLexica.RECORD, s.toString());
        if (s.equals("pointer")) return new Token(CatLexica.POINTER, s.toString());
        if (s.equals("null")) return new Token(CatLexica.NULL, s.toString());
        if (s.equals("new")) return new Token(CatLexica.NEW, s.toString());
        if (s.equals("dispose")) return new Token(CatLexica.DISPOSE, s.toString());
        if (s.equals("procedure")) return new Token(CatLexica.PROC, s.toString());
        if (s.equals("var")) return new Token(CatLexica.VAR, s.toString());
        if (s.equals("forward")) return new Token(CatLexica.FORWARD, s.toString());
        //Si no es palabra reservada, se devuelve como identificador
        return new Token(CatLexica.IDENT, s.toString());
    }

    //Comprueba si la palabra es uno de los cuatro castings posibles y lo devuelve como token en caso de serlo.
    //Si no lo es, significa que hay que descomponer el posible token en tres:
    //parentesis de apertura, identificador y pararentesis de cierre.
    private Token analizaCasting(String s) {
        s = s + (char)sigCar;
        if (s.equals("(nat)")) return new Token(CatLexica.CONV_NAT, s.toString());
        if (s.equals("(int)")) return new Token(CatLexica.CONV_INT, s.toString());
        if (s.equals("(float)")) return new Token(CatLexica.CONV_FLOAT, s.toString());
        if (s.equals("(char)")) return new Token(CatLexica.CONV_CHAR, s.toString());
        return null;
    }
}